package com.essearch.service.impl;

import com.essearch.common.TableConst;
import com.essearch.common.TableFieldEnum;
import com.essearch.page.Page;
import com.essearch.service.BaseSearchService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;

/**
 * elasticsearch 搜索引擎
 */
@Service
public class BaseSearchServiceImpl<T> implements BaseSearchService<T> {
    private Logger log = LoggerFactory.getLogger(getClass());

    private static final String[] indexNames = {"st20201010_item_main","st20201010_jhrw_item"};
    private static final String[] fieldNames = {"itemName","jhrwName","qwcx"};
    private static final String personIndex = "st20201010_item_person_base";
    private static final String jhrwUnitIndex = "st20201010_jhrw_unit";

    @Resource
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public List<Object> query(String keyword, Class... clazzs) {
        List<Object> list = new ArrayList();
        Arrays.stream(clazzs).forEach(clazz -> {
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withQuery(new QueryStringQueryBuilder(keyword))
                    .withSort(SortBuilders.scoreSort().order(SortOrder.DESC))
//                .withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC))
                    .build();
            list.addAll(elasticsearchTemplate.queryForList(searchQuery,clazz));
        });
        return list;
    }

    /**
     * 高亮显示
     */
    @Override
    public  List<Map<String,Object>> queryHit(String keyword,String[] indexNames,String[] fieldNames) {
        // 构造查询条件,使用标准分词器.
        QueryBuilder matchQuery = createQueryBuilder(keyword,new ArrayList<>(),fieldNames);

        // 设置高亮,使用默认的highlighter高亮器
        HighlightBuilder highlightBuilder = createHighlightBuilder(fieldNames);

        // 设置查询字段
        SearchResponse response = elasticsearchTemplate.getClient().prepareSearch(indexNames)
                .setQuery(matchQuery)
                .highlighter(highlightBuilder)
                .setSize(10000)
                .get();

        // 返回搜索结果
        SearchHits hits = response.getHits();

        return getHitList(hits,keyword);
    }


    private List<String> queryList(String queryIndex, String queryField, String targetField, String filterField, String queryValue) {
        List<String> idList = new ArrayList<>();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchPhraseQuery(queryField, queryValue))
                .must(QueryBuilders.matchPhraseQuery(filterField, 1));
        CardinalityAggregationBuilder aggregationBuilder = AggregationBuilders.cardinality("uid").field("id");
//        HighlightBuilder highlightBuilder = createHighlightBuilder(queryField);
        // 设置查询字段
        SearchResponse response = elasticsearchTemplate.getClient().prepareSearch(queryIndex)
                .setQuery(queryBuilder)
                .addAggregation(aggregationBuilder)
//                .highlighter(highlightBuilder)
                .setFrom(1)
                .setSize(10000)
                .get();
        // 返回搜索结果
        Arrays.stream(response.getHits().getHits()).forEach(data -> idList.add(data.getSourceAsMap().get(targetField).toString()));
        return idList;
    }
    /**
     * 高亮显示，返回分页
     */
    @Override
    public Page<Map<String, Object>> queryHitByPage(int pageNo,int pageSize, String keyword, String type) {
        List<String> idList = new ArrayList<>();
        switch (type){
            case TableConst.FIELD_AUTHOR:
                idList = queryList(personIndex, TableFieldEnum.PERSON.title(), TableFieldEnum.PERSON.targetField(),
                        TableFieldEnum.PERSON.filterField(), keyword);
                if (idList.size() == 0){
                    return new Page<>(pageNo,pageSize,0);
                }
                break;
            case TableConst.FIELD_COMPANY:
                idList = queryList(jhrwUnitIndex, TableFieldEnum.JHRW_UNIT.title(), TableFieldEnum.JHRW_UNIT.targetField(),
                        TableFieldEnum.JHRW_UNIT.filterField(), keyword);
                if (idList.size() == 0){
                    return new Page<>(pageNo,pageSize,0);
                }
                break;
            case TableConst.FIELD_CONTENT:
            default:
        }

        // 构造查询条件
        QueryBuilder matchQuery = createQueryBuilder(keyword,idList,fieldNames);

        CardinalityAggregationBuilder aggregationBuilder = AggregationBuilders.cardinality("uid").field("id");
        // 设置高亮,使用默认的highlighter高亮器
        HighlightBuilder highlightBuilder = createHighlightBuilder(fieldNames);

        // 设置查询字段
        SearchResponse response = elasticsearchTemplate.getClient().prepareSearch(indexNames)
                .setQuery(matchQuery)
                .highlighter(highlightBuilder)
                .addAggregation(aggregationBuilder)
                .setFrom((pageNo-1) * pageSize)
                .setSize(pageSize)
                .get();


        // 返回搜索结果
        SearchHits hits = response.getHits();

        Long totalCount = hits.getTotalHits();
        Page<Map<String, Object>> page = new Page<>(pageNo,pageSize,totalCount.intValue());
        page.setList(getHitList(hits, keyword));
        return page;
    }

    /**
     * 构造查询条件
     */
    private QueryBuilder createQueryBuilder(String keyword, List<String> idList, String... fieldNames){
        // 构造查询条件,使用标准分词器. // matchQuery(),单字段搜索
//        return QueryBuilders.multiMatchQuery(keyword,fieldNames)
////                .analyzer("ik_max_word")
//                .analyzer("ik_smart")
//                .operator(Operator.OR);

        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        Arrays.stream(fieldNames).forEach(fieldName -> queryBuilder.should(QueryBuilders.matchPhraseQuery(fieldName,keyword)));
        idList.forEach(itemId -> queryBuilder.should(QueryBuilders.matchPhraseQuery("itemId",itemId)));
        return queryBuilder;
    }
    /**
     * 构造高亮器
     */
    private HighlightBuilder createHighlightBuilder(String... fieldNames){
        // 设置高亮,使用默认的highlighter高亮器
        HighlightBuilder highlightBuilder = new HighlightBuilder()
                .preTags("<span style='color:red'>")
                .postTags("</span>");

        // 设置高亮字段
        for (String fieldName: fieldNames) highlightBuilder.field(fieldName);

        return highlightBuilder;
    }

    /**
     * 处理高亮结果
     */
    private List<Map<String,Object>> getHitList(SearchHits hits, String keyword){
        List<Map<String,Object>> list = new ArrayList<>();
        Map<String,Object> map;
        for(SearchHit searchHit : hits){
            map = new HashMap<>();
            // 源数据
            map.put("source",searchHit.getSourceAsMap());
            // 处理高亮数据
            StringBuilder highlight = new StringBuilder();
            StringBuilder titleSb = new StringBuilder();
            searchHit.getHighlightFields().forEach((k,v) -> {
                for(Text text : v.getFragments()) {
                    if (!highlight.toString().equals(text.toString()+"&nbsp;")){
                        highlight.append(text.string()).append("&nbsp;");
                    }
                    if (titleSb.length() == 0){
                        titleSb.append(text.string());
                    }
                }
            });
            // 标题、内容
            String title = null;
            String content = null;
            for (TableFieldEnum value : TableFieldEnum.values()) {
                if (value.type().equals(searchHit.getType())){
                    ArrayList<String> dataList = formatData(keyword, (String) searchHit.getSourceAsMap().get(value.title()), (String) searchHit.getSourceAsMap().get(value.content()));
                    if (dataList.size()>0){
                        title = dataList.get(0);
                        if (StringUtils.isEmpty(title)){
                            title = titleSb.toString();
                        }
                        content = dataList.get(1);
                    }
                    break;
                }
            }
            map.put("highlight",highlight.length() == 0 ? title : highlight);
            map.put("title",title);
            map.put("content",content);
            list.add(map);
        }
        return list;
    }

    private ArrayList<String> formatData(String keyword, String... fields){
        ArrayList<String> list = new ArrayList<>();
        if (fields.length == 0){
            return list;
        } else {
            //按分词结果高亮
//            AnalyzeRequest analyzeRequest = new AnalyzeRequest()
////                    .text(keyword).analyzer("ik_max_word");
//                    .text(keyword).analyzer("ik_smart");
//            List<AnalyzeResponse.AnalyzeToken> tokens = elasticsearchTemplate.getClient().admin().indices().analyze(analyzeRequest).actionGet().getTokens();


            //按搜索词 全词高亮
            Arrays.stream(fields).forEach(filed -> {
                if (!StringUtils.isEmpty(filed)){
                    if (filed.contains(keyword)){
                        filed = filed.replaceAll(keyword,"<span style='color:red'>" + keyword + "</span>");
                    }
                    if (!filed.contains("text-indent")){
                        filed = filed.replaceAll("\n","</p><p style='text-indent:24.0000pt;'>");
                    }
                } else {
                    filed = "<span style='color:#cccfcd''>无内容</span>";
                }
                list.add(filed);
            });
            return list;
        }
    }

    @Override
    public void deleteIndex(String indexName) {
        elasticsearchTemplate.deleteIndex(indexName);
    }
}
